<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>试试小动画</title>
  <style>
    .box{
      position: relative;
      width: 120px;
      height: 120px;
      background: royalblue;
      margin: 80px auto;
      text-align: center;
      line-height: 120px;
      color: #fff;
    }
    .hh{
      height: 360px;
    }
  </style>
</head>
<body>
<div class="box a0">BASE</div>
<div class="box a1">SETINIT</div>
<div class="box a2">直线</div>
<div class="box a3">加速</div>
<div class="box a4">减速</div>
<div class="box a5">抛物线</div>
<div class="box a6">摆动</div>
<div class="box a7">正弦</div>
<div class="box a8">圆周</div>
<div class="box a9">fun</div>
<div class="box a10">折线</div>
<div class="box a11">动画队列</div>
<div class="box a12">折线循环</div>
<div class="box a13">下坠弹跳</div>
<div class="box a14">损耗</div>
<div class="box a15">滚动</div>

<div class="hh"></div>
<script>
  /* 拼了老命在转啊~ */
  var deg = 0;
  document.getElementsByClassName('a0')[0].addEventListener('click', function(){
    var self = this;
    requestAnimationFrame(function change(){
      self.style.transform = 'rotate(' + (deg++) +'deg)';
      requestAnimationFrame(change);
    });
  });

  /* 优化？ */
  document.getElementsByClassName('a1')[0].addEventListener('click', function(){
    var self = this, startTime = Date.now(),
        duration = 1000;
    setInterval(function(){
      var p = (Date.now() - startTime) / duration;
      self.style.transform = 'rotate(' + (360 * p) +'deg)';
    }, 1000/60);
  });

  /* 匀速直线运动 */
  document.getElementsByClassName('a2')[0].addEventListener('click', function(){
    var self = this, startTime = Date.now(),
        distance = 200, T = 2000;
    requestAnimationFrame(function step(){
      var p = Math.min(1.0, (Date.now() - startTime) / T);
      self.style.transform = 'translateX(' + (distance * p) +'px)';
      if(p < 1.0) requestAnimationFrame(step);
    });
  });

  /* 匀加速 */
  document.getElementsByClassName('a3')[0].addEventListener('click', function(){
    var self = this, startTime = Date.now(),
        distance = 200, T = 2000;
    requestAnimationFrame(function step(){
      var p = Math.min(1.0, (Date.now() - startTime) / T);
      self.style.transform = 'translateX(' + (distance * p * p) +'px)';
      if(p < 1.0) requestAnimationFrame(step);
    });
  });

  /* 匀减速 */
  document.getElementsByClassName('a4')[0].addEventListener('click', function(){
    var self = this, startTime = Date.now(),
        distance = 200, T = 2000;
    requestAnimationFrame(function step(){
      var p = Math.min(1.0, (Date.now() - startTime) / T);
      self.style.transform = 'translateX('
          + (distance * p * (2-p)) +'px)';
      if(p < 1.0) requestAnimationFrame(step);
    });
  });

  // 抛物线运动
  document.getElementsByClassName('a5')[0].addEventListener('click', function(){
    var self = this, startTime = Date.now(),
        disX = 200, disY = 200,
        T = 1000 * Math.sqrt(2 * disY / 98);
    //假设10px是1米，disY = 20米

    requestAnimationFrame(function step(){
      var p = Math.min(1.0, (Date.now() - startTime) / T);
      var tx = disX * p;
      var ty = disY * p * p;

      self.style.transform = 'translate('
          + tx + 'px' + ',' + ty +'px)';
      if(p < 1.0) requestAnimationFrame(step);
    });
  });

  // 简单的摆动
  document.getElementsByClassName('a6')[0].addEventListener('click', function(){
    var self = this, startTime = Date.now(),
        distance = 100,
        T = 2000;

    requestAnimationFrame(function step(){
      var p = Math.min(1.0, (Date.now() - startTime) / T);
      var tx = distance * Math.sin(2 * Math.PI * p);

      self.style.transform = 'translateX('
          + tx + 'px)';
      if(p < 1.0) requestAnimationFrame(step);
    });
  });

  // 正弦
  document.getElementsByClassName('a7')[0].addEventListener('click', function(){
    var self = this, startTime = Date.now(),
        distance = 100,
        T = 2000;

    requestAnimationFrame(function step(){
      var p = Math.min(1.0, (Date.now() - startTime) / T);
      var ty = distance * Math.sin(2 * Math.PI * p);
      var tx = 2 * distance * p;

      self.style.transform = 'translate('
          + tx + 'px,' + ty + 'px)';
      if(p < 1.0) requestAnimationFrame(step);
    });
  });

  // 圆周
  document.getElementsByClassName('a8')[0].addEventListener('click', function(){
    var self = this, startTime = Date.now(),
        r = 100, T = 2000;

    requestAnimationFrame(function step(){
      var p = Math.min(1.0, (Date.now() - startTime) / T);
      var tx = -r * Math.sin(2 * Math.PI * p),
          ty = -r * Math.cos(2 * Math.PI * p);

      self.style.transform = 'translate('
          + tx + 'px,' + ty + 'px)';
      if(p < 1.0) requestAnimationFrame(step);
    });
  });


  /**
   * 重要总结：
   * 动画时长：T
     动画进程：p=t/T (p∈[0,1])
     easing：e=f(p)
     动画方程：[x,y]=G(e)
     动画开始、进行中、结束：onStart、onProgress、onFinished
   */

  // 动画的简易封装
  function Animator(duration, progress, easing){
    this.duration = duration;
    this.progress = progress;
    this.easing = easing || function(p){return p};
  }

  Animator.prototype = {
    start: function(finished){
      var startTime = Date.now();
      var duration = this.duration,
          self = this;

      requestAnimationFrame(function step(){
        var p = (Date.now() - startTime) / duration;
        var next =  true;

        if(p < 1.0){
          self.progress(self.easing(p), p);
        }else{
          if(typeof finished === 'function'){
            next = finished() === false;
          }else{
            next = finished === false;
          }

          if(!next){
            self.progress(self.easing(1.0), 1.0);
          }else{
            startTime += duration;
            self.progress(self.easing(p), p);
          }
        }

        if(next) requestAnimationFrame(step);
      });
    }
  };

  // 试一下
  var animator = new Animator(2000,  function(p){
    var tx = -100 * Math.sin(2 * Math.PI * p),
        ty = -100 * Math.cos(2 * Math.PI * p);
    document.getElementsByClassName('a9')[0].style.transform = 'translate('
        + tx + 'px,' + ty + 'px)';
  });
  document.getElementsByClassName('a9')[0].addEventListener('click', function(){
    animator.start(false);
  });


  // 折线动画
  (function () {
    var block = document.getElementsByClassName('a10')[0];

    var a1 = new Animator(1000,  function(p){
      var tx = 100 * p;
      block.style.transform = 'translateX('
          + tx + 'px)';
    });

    var a2 = new Animator(1000,  function(p){
      var ty = 100 * p;
      block.style.transform = 'translate(100px,'
          + ty + 'px)';
    });

    block.addEventListener('click', function(){
      a1.start(function(){
        a2.start();
      });
    });
  })();


  /**
   * =============================
   * 动画队列
   * =============================
   *  基础版本
      AnimationQueue.prototype = {
        append: function(){
          var args = [].slice.call(arguments);
          this.animators.push.apply(this.animators, args);
        },
        flush: function(){
          if(this.animators.length){
            var self = this;
            function play(){
              var animator = self.animators.shift();
              animator.start(function(){
                if(self.animators.length) play();
              });
            }
            play();
          }
        }
      };
   */

  function AnimationQueue(animators){
    this.animators = animators || [];
  }

  AnimationQueue.prototype = {
    append: function(){
      var args = [].slice.call(arguments);
      this.animators.push.apply(this.animators, args);
    },
    flush: function(){
      if(this.animators.length){
        var self = this;
        function play(){
          var animator = self.animators.shift();
          if(animator instanceof Animator){
            animator.start(function(){
              if(self.animators.length) play();
            });
          }else{
            animator.apply(self);
            if(self.animators.length) play();
          }
        }
        play();
      }
    }
  };

  // 试试动画队列
  (function () {
    var block = document.getElementsByClassName('a11')[0];

    var a1 = new Animator(1000,  function(p){
      var tx = 100 * p;
      block.style.transform = 'translateX('
          + tx + 'px)';
    });

    var a2 = new Animator(1000,  function(p){
      var ty = 100 * p;
      block.style.transform = 'translate(100px,'
          + ty + 'px)';
    });

    var a3 = new Animator(1000,  function(p){
      var tx = 100 * (1-p);
      block.style.transform = 'translate('
          + tx + 'px, 100px)';
    });

    var a4 = new Animator(1000,  function(p){
      var ty = 100 * (1-p);
      block.style.transform = 'translateY('
          + ty + 'px)';
    });

    block.addEventListener('click', function(){
      var animators = new AnimationQueue();
      animators.append(a1, a2, a3, a4);
      animators.flush();
    });
  })();

  // 折线循环
  (function () {
    var block = document.getElementsByClassName('a12')[0];

    var a1 = new Animator(1000,  function(p){
      var tx = 100 * p;
      block.style.transform = 'translateX('
          + tx + 'px)';
    });

    var a2 = new Animator(1000,  function(p){
      var ty = 100 * p;
      block.style.transform = 'translate(100px,'
          + ty + 'px)';
    });

    var a3 = new Animator(1000,  function(p){
      var tx = 100 * (1-p);
      block.style.transform = 'translate('
          + tx + 'px, 100px)';
    });

    var a4 = new Animator(1000,  function(p){
      var ty = 100 * (1-p);
      block.style.transform = 'translateY('
          + ty + 'px)';
    });


    block.addEventListener('click', function(){
      var animators = new AnimationQueue();
      animators.append(a1, a2, a3, a4, function(){
        this.append(a1, a2, a3, a4, arguments.callee);
      });
      animators.flush();
    });
  })();

  // 弹跳动画
  (function () {
    var block = document.getElementsByClassName('a13')[0];

    var a1 = new Animator(1414,  function(p){
      var ty = 200 * p * p;
      block.style.transform = 'translateY('
          + ty + 'px)';
    });

    var a2 = new Animator(1414,  function(p){
      var ty = 200 - 200 * p * (2-p);
      block.style.transform = 'translateY('
          + ty + 'px)';
    });

    block.addEventListener('click', function(){
      var animators = new AnimationQueue();
      animators.append(a1,a2, function(){
        this.append(a1, a2, arguments.callee);
      });
      animators.flush();
    });
  })();

  // 能量损耗
  (function () {
    var block = document.getElementsByClassName('a14')[0];

    block.addEventListener('click', function(){
      var T = 1414;

      var a1 = new Animator(T,  function(p){
        var s = this.duration * 200 / T;
        var ty = s * (p * p - 1);
        block.style.transform = 'translateY('
            + ty + 'px)';
      });

      var a2 = new Animator(T,  function(p){
        var s = this.duration * 200 / T;
        var ty = - s * p * (2-p);
        block.style.transform = 'translateY('
            + ty + 'px)';
      });

      var animators = new AnimationQueue();
      function foo(){
        a2.duration *= 0.7;
        if(a2.duration <= 0.0001){
          console.log('done');
          animators.animators.length = 0;
        }
      }
      animators.append(a1 ,foo, a2,
          function b(){
            a1.duration *= 0.7;
            this.append(a1, foo, a2, b);
          });
      animators.flush();
    });
  })();

  // 滚动
  (function () {
    var block = document.getElementsByClassName('a15')[0];

    var a1 = new Animator(4000, function(p){
      var rotation = 'rotate(' + 720 * p + 'deg)';
      var x = 50 + 314 * p + 'px';

      block.style.transform = rotation;
      block.style.left = x;
    });

    block.addEventListener('click', function(){
      a1.start();
    });
  })();




</script>
</body>
</html>